﻿using System;
using System.Threading;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MassSpectrum.Controller;
using MassSpectrum.dto;
using ZedGraph;

namespace MassSpectrum
{
    public partial class MainForm : Form
    {

        const uint LIMIT = 100;
        /// <summary>
        /// Панель на которой рисуется график
        /// </summary>
        GraphPane pane = new GraphPane();

        double _currentx = 0;
        double minScaleValue = 0.0;
        double maxScaleValue = 4096;

        private Data d = null;
        private Thread thread;

        private static  double currentMass = 0.0; // текущее значение mass для усреднения
        private static double currentTemp = 0.0;
        private static double currentHeating = 0.0;

        private bool reqStop = true;
        bool threadRunning = false; /* Признак, идет ли сбор данных в отдельном потоке */
        bool ReadFromADCFalg = false; // Признак, определяющий были ли загружены данные из файла или с АЦП

        private IList<Data> Meashurement = new List<Data>();
        private PointPairList _data = new PointPairList();

        private delegate void finishThreadDelegate();
        private delegate void updateDateDelegate();


        /// <summary>
        /// Обработчик события PointValueEvent.
        /// Должен вернуть строку, которая будет показана во всплывающей подсказке
        /// </summary>
        /// <param name="sender">Отправитель сообщения</param>
        /// <param name="pane">Панель для рисования</param>
        /// <param name="curve">Кривая, около которой находится курсор</param>
        /// <param name="iPt">Номер точки в кривой</param>
        /// <returns>Нужно вернуть отображаемую строку</returns>
        string zedGraph_PointValueEvent(ZedGraphControl sender, GraphPane pane, CurveItem curve, int iPt)
        {
            // Получим точку, около которой находимся
            PointPair point = curve[iPt];

            // Сформируем строку
            string result = string.Format("Масса [m/q]: {0:F3}", point.Tag);

            return result;
        }


        /// <summary>
        /// Подготовим область рисования 
        /// </summary>
        private void PrepareGraph()
        {
            pane.Title.Text = "";
            pane.XAxis.Title.Text = "Номер измерения";
            pane.YAxis.Title.Text = "Интенсивность [В]";
            // Включаем отображение сетки напротив крупных рисок по оси X
            pane.XAxis.MajorGrid.IsVisible = true;
            // Задаем вид пунктирной линии для крупных рисок по оси X:
            // Длина штрихов равна 10 пикселям, ... 
            pane.XAxis.MajorGrid.DashOn = 10;
            // затем 5 пикселей - пропуск
            pane.XAxis.MajorGrid.DashOff = 5;

            // Включаем отображение сетки напротив крупных рисок по оси Y
            pane.YAxis.MajorGrid.IsVisible = true;
            // Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
            pane.YAxis.MajorGrid.DashOn = 10;
            pane.YAxis.MajorGrid.DashOff = 5;
            // Включаем отображение сетки напротив мелких рисок по оси X
            pane.YAxis.MinorGrid.IsVisible = true;
            // Задаем вид пунктирной линии для крупных рисок по оси Y: 
            // Длина штрихов равна одному пикселю, ... 
            pane.YAxis.MinorGrid.DashOn = 1;

            // затем 2 пикселя - пропуск
            pane.YAxis.MinorGrid.DashOff = 2;

            // Включаем отображение сетки напротив мелких рисок по оси Y
            pane.XAxis.MinorGrid.IsVisible = true;

            // Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
            pane.XAxis.MinorGrid.DashOn = 1;
            pane.XAxis.MinorGrid.DashOff = 2;
            // По оси Y установим автоматический подбор масштаба
            pane.YAxis.Scale.MinAuto = true;
            pane.YAxis.Scale.MaxAuto = true;
            //установим начало координат х в нуль
            pane.XAxis.Scale.Min = 0;
            // !!! Установим значение параметра IsBoundedRanges как true.
            // !!! Это означает, что при автоматическом подборе масштаба 
            // !!! нужно учитывать только видимый интервал графика
            pane.IsBoundedRanges = true;

            Graph.GraphPane = pane;
        }

        public MainForm()
        {
            InitializeComponent();

            ProcessFactory.path = Application.StartupPath.ToString() + "//" + DateTime.Now.ToString("dd/MM/yyyy HH-mm-ss") + ".txt";
            PrepareGraph();
            // Включим показ всплывающих подсказок при наведении курсора на график
            Graph.IsShowPointValues = true;

            // Будем обрабатывать событие PointValueEvent, чтобы изменить формат представления координат
            Graph.PointValueEvent +=
                new ZedGraphControl.PointValueHandler(zedGraph_PointValueEvent);


           bool enabled =  ProcessFactory.GetConnection();

            if (enabled)
            {
               
                lbl_device_status.Text = "Устройство доступно и настроено";
                btn_start.Enabled = true;
                btn_clear.Enabled = true;
               
            }

            Graph.IsShowHScrollBar = true;
            Graph.IsAutoScrollRange = true;
            Graph.IsShowVScrollBar = true;
            Graph.ScrollGrace = .05;
        }

        private void threadfunc()
        {
          
            while (!reqStop)
            {
                this.d = ProcessFactory.GetMeasurement();
                Meashurement.Add(d);

                if (Meashurement.Count == LIMIT)
                {
                    foreach(Data dat in Meashurement)
                    {
                        currentMass += dat.Mass;
                        currentHeating += dat.Heating;
                        currentTemp += dat.Temperature;
                    }
                    currentMass /= LIMIT;
                    currentTemp /= LIMIT;
                    currentHeating /= LIMIT;
                    
                    ProcessFactory.WriteMeasurement(Meashurement);
                    Meashurement.Clear();
                }
                           

                DrawGraph();
            }

         finishThread();
        }

        /* Функция, вызываемая по завершению потока сбора данных.
         * Так как она работает с интерфейсом, то при вызове из другого потока,
         * она планируется на выполнения в основном потоке */
        private void finishThread()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new finishThreadDelegate(this.finishThread));
               
            }
            else
            {
                threadRunning = false;
                DrawGraph();
            }


        }

        private void DrawGraph()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new updateDateDelegate(DrawGraph));
            }
            else
            {

                _data.Add(_currentx, d.Intensity, d.Mass.ToString("####0.#####"));

                l_intensity.Text = d.Intensity.ToString("#####0.######");
                l_mass.Text = currentMass.ToString("####0.00#");
                l_temperature.Text = currentTemp.ToString("####0.0000#");
                l_heating.Text = currentHeating.ToString("####0.0000#");
                
                _currentx++;
                // Рассчитаем интервал по оси X, который нужно отобразить на графике
                
                
                if (_currentx > 4096)
                {
                    minScaleValue = maxScaleValue - 4096;
                    maxScaleValue = _currentx;    
                }

                
                GraphPane pane = Graph.GraphPane;
                pane.XAxis.Scale.Min = minScaleValue;
                pane.XAxis.Scale.Max = maxScaleValue;
                pane.CurveList.Clear();
                pane.AddCurve("Интенсивность", _data, Color.Blue, SymbolType.None);
                // Обновим оси
                Graph.AxisChange();

                // Обновим сам график
                Graph.Invalidate();
            }
        }

     

        private void btn_start_Click(object sender, EventArgs e)
        {
            ReadFromADCFalg = true;
            if (reqStop)
            {
                reqStop = false;
                ProcessFactory.StartMeashurement();
                this.thread = new Thread(threadfunc);
                threadRunning = true;

                this.thread.Start();
                btn_start.Text = "Стоп";
                disableElements();
                
            }
            else
            {
               
                btn_start.Text = "Старт";
                ElementSetEnabled();
                reqStop = true;

                if (ReadFromADCFalg)
                {
                    ProcessFactory.oldpath = ProcessFactory.path;
                    ProcessFactory.WriteMeasurement(Meashurement);
                    ProcessFactory.path = Application.StartupPath.ToString() + "//" + DateTime.Now.ToString("dd/MM/yyyy HH-mm-ss") + ".txt";
                    
                }

            }
        }

        private void ElementSetEnabled()
        {
            menu.Enabled = true;
            btn_clear.Enabled = true;
        }

        private void disableElements()
        {
          
            menu.Enabled = false;
            btn_clear.Enabled = false;

        }

        

      
        private void btn_load_data_Click(object sender, EventArgs e)
        {
         

            OpenFileDialog mydialog = new OpenFileDialog();
            mydialog.CheckFileExists = true;
            mydialog.Multiselect = false;

            if (mydialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {

               Meashurement = ProcessFactory.ReadFromFile(mydialog.FileName);
               DrawGraphFromFile(Meashurement);
               btn_clear.Enabled = true;
               ReadFromADCFalg = false;
            }

            ProcessFactory.path = mydialog.FileName;

        }

        private void DrawGraphFromFile(IList<Data> Meashurement)
        {
            // Очистим список кривых на тот случай, если до этого сигналы уже были нарисованы
            pane.CurveList.Clear();

            // Создадим список точек
            _data = new PointPairList();

            uint counter = 0;

            if (Meashurement != null)
            {
                foreach (Data p in Meashurement)
                {

                    _data.Add((double)counter, (double)p.Intensity, p.Mass.ToString());
                    counter++;
                }
            }

            else
            {
                MessageBox.Show("");
            }

            // Создадим кривую, 
            // которая будет рисоваться голубым цветом (Color.Blue),
            // Опорные точки выделяться не будут (SymbolType.None)
            LineItem myCurve = pane.AddCurve("Интенсивность", _data, Color.Blue, SymbolType.None);


            // Вызываем метод AxisChange (), чтобы обновить данные об осях. 
            // В противном случае на рисунке будет показана только часть графика, 
            // которая умещается в интервалы по осям, установленные по умолчанию
            Graph.AxisChange();

            // Обновляем график
            Graph.Invalidate();
        }

        private void btn_save_Click(object sender, EventArgs e)
        {
            if (_data.Count == 0)
            {
                MessageBox.Show("Нет данных для сохранения!");
                return;
            }
            
            if(!ReadFromADCFalg)
            {
                MessageBox.Show("Данные с АЦП не были получены \n Отображаемые данные уже были сохранены!");
                return;
            }

            SaveFileDialog mydialog = new SaveFileDialog();

            mydialog.InitialDirectory = Application.StartupPath;
            mydialog.DefaultExt = ".txt";
            mydialog.AddExtension = true;
            
            if (mydialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                ProcessFactory.WriteFile(mydialog.FileName);
            }
            btn_clear_Click(sender, e);
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            //останов сбора данных 
            if (threadRunning)
            {
                reqStop = true;

                /* ожидаем завершения потока. Так как 
                 * поток работает с GUI и планирует выполнение части
                 * функций в основном потоке, то мы не можем сдесь просто
                 * сделать Join, вызываем Application.DoEvents(), чтобы
                 * в нем обработать запланированные UpdateData()/finishThread() */
                while (threadRunning)
                {
                    Application.DoEvents();
                }
            }

            if(d!=null)

            ProcessFactory.StopMeasurement();
        }

        private void btn_clear_Click(object sender, EventArgs e)
        {
            PrepareGraph();
          
            this.Graph.GraphPane.CurveList.Clear();
            this.Graph.GraphPane.GraphObjList.Clear();
            // Обновим оси
            Graph.AxisChange();
            // Обновим сам график
            Graph.Invalidate();

            Meashurement.Clear();
            _data.Clear();
            _currentx = 0;
             minScaleValue = 0.0;
             maxScaleValue = 4096;
            
        }



     
    }
}
